package com.test.sshshiro.framework;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Expression;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.ObjectRetrievalFailureException;

import com.test.sshshiro.framework.annotation.ColumnQuery;
import com.test.sshshiro.framework.page.BaseQuery;
import com.test.sshshiro.framework.page.Page;
import com.test.sshshiro.framework.page.PageRequest;
import com.test.sshshiro.util.SqlRemoveUtils;

import javacommon.xsqlbuilder.SafeSqlProcesser;
import javacommon.xsqlbuilder.SafeSqlProcesserFactory;
import javacommon.xsqlbuilder.XsqlBuilder;
import javacommon.xsqlbuilder.XsqlBuilder.XsqlFilterResult;
import javacommon.xsqlbuilder.safesql.DirectReturnSafeSqlProcesser;

public abstract class BaseHibernateDAO<E, PK extends Serializable> implements EntityDAO<E, PK> {

	public Class getEntityClass() {
		Class c = getClass();
		Type t = c.getGenericSuperclass();
		if (t instanceof ParameterizedType) {
			Type[] p = ((ParameterizedType) t).getActualTypeArguments();
			return (Class<E>) p[0];
		}

		return null;
	}
	
	@Autowired
	@Qualifier("sessionFactory")
	private SessionFactory sessionFactory;
	
	public Session getSession() {
		// 事务必须是开启的，否则获取不到

		Session session = sessionFactory.getCurrentSession();

		return session;
	}
	
	@Override
	public List<E> findAllBy(final String[] propertyNames, final Object[] values){
		Criteria criteria = getSession().createCriteria(getEntityClass());
		if(propertyNames == null || values == null || propertyNames.length != values.length){
			return null;
		}
		for(int i = 0;i<propertyNames.length;i++)
		{
			criteria.add(Expression.eqOrIsNull(propertyNames[i], values[i]));
		}
		return criteria.list();
	}
	
	@Override
	public List<E> find(String hql, Map<String, Object> values) {

		if(values == null){
			values = new HashMap<String,Object>();
		}
		Query query = getSession().createQuery(hql);
		query.setProperties(values);
		return query.list();
	}
	
	@Override
	public List<E> findByHql(String hql,Object[] values){
		Query query = getSession().createQuery(hql);
		if(values != null){
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query.list();
	}
	
	@Override
	public Page findPage(BaseQuery baseQuery) {
		String hql = generateQueryCondition(baseQuery, null, null, null);
		if(hql == null){
			return null;
		}
		hql = "select t from " + getEntityClass().getSimpleName() + " t where 1 = 1 " + hql;
		
		return pageQuery(hql, baseQuery);
		
	}
	
	@Override
	public Page pageQuery(final String sql, final PageRequest pageRequest) {
		final String countQuery = "select count(*) "
				+ SqlRemoveUtils.removeSelect(SqlRemoveUtils.removeFetchKeyword((sql)));
		return pageQuery(sql, countQuery, pageRequest);
	}
	
	@Override
	public Page pageQuery(final String sql, final PageRequest pageRequest,final Map customFilters) {
		final String countQuery = "select count(*) "
				+ SqlRemoveUtils.removeSelect(SqlRemoveUtils.removeFetchKeyword((sql)));
		return pageQuery(sql, countQuery, pageRequest,customFilters);
	}
	
	@Override
	public Page pageQuery(final String sql, String countQuery, final PageRequest pageRequest) {
		Map otherFilters = new HashMap(1);
		otherFilters.put("sortColumns", pageRequest.getSortColumns());

		XsqlBuilder builder = getXsqlBuilder();

		XsqlFilterResult queryXsqlResult = builder.generateHql(sql, otherFilters, pageRequest);
		XsqlFilterResult countQueryXsqlResult = builder.generateHql(countQuery, otherFilters, pageRequest);

		return PageQueryUtils.pageQuery(getSession(), pageRequest, queryXsqlResult, countQueryXsqlResult);
	}
	
	@Override
	public Page pageQuery(final String sql, String countQuery, final PageRequest pageRequest,final Map customFilters) {
		Map otherFilters = new HashMap(1);
		otherFilters.put("sortColumns", pageRequest.getSortColumns());
		otherFilters.putAll(customFilters);

		XsqlBuilder builder = getXsqlBuilder();

		XsqlFilterResult queryXsqlResult = builder.generateHql(sql, otherFilters, pageRequest);
		XsqlFilterResult countQueryXsqlResult = builder.generateHql(countQuery, otherFilters, pageRequest);

		return PageQueryUtils.pageQuery(getSession(), pageRequest, queryXsqlResult, countQueryXsqlResult);
	}
	
	public XsqlBuilder getXsqlBuilder() {

		SessionFactoryImpl sf = (SessionFactoryImpl) (getSession().getSessionFactory());
		Dialect dialect = sf.getDialect();

		// or SafeSqlProcesserFactory.getMysql();
		SafeSqlProcesser safeSqlProcesser = SafeSqlProcesserFactory.getFromCacheByHibernateDialect(dialect);
		XsqlBuilder builder = new XsqlBuilder(safeSqlProcesser);

		if (builder.getSafeSqlProcesser().getClass() == DirectReturnSafeSqlProcesser.class) {
			System.err.println(BaseHibernateDAO.class.getSimpleName()
					+ ".getXsqlBuilder(): ���ⱨ��,��δ����Sql��ȫ����,����ŵ�ת���ַ���ƴ��sqlʱ��Ҫת��,��Ȼ�ᵼ��Sqlע�빥���İ�ȫ���⣬���޸�Դ��ʹ��new XsqlBuilder(SafeSqlProcesserFactory.getDataBaseName())������ȫ����");
		}
		return builder;
	}
	
	static class PageQueryUtils {
		private static Page pageQuery(Session session, final PageRequest pageRequest,
				final XsqlFilterResult queryXsqlResult, final XsqlFilterResult countQueryXsqlResult) {

			Query query = setQueryParameters(session.createQuery(queryXsqlResult.getXsql()), pageRequest);
			Query countQuery = setQueryParameters(
					session.createQuery(SqlRemoveUtils.removeOrders(countQueryXsqlResult.getXsql())), pageRequest);

			return (Page) executeQueryForPage(pageRequest, query, countQuery);

		}

		private static Object executeQueryForPage(final PageRequest pageRequest, Query query, Query countQuery) {
			Page page = new Page(pageRequest, ((Number) countQuery.uniqueResult()).intValue());
			if (page.getTotalCount() <= 0) {
				page.setResult(new ArrayList(0));
			} else {
				page.setResult(query.setFirstResult(page.getFirstResult()).setMaxResults(page.getPageSize()).list());
			}
			return page;
		}

		public static Query setQueryParameters(Query q, Object params) {
			q.setProperties(params);
			return q;
		}

		public static Query setQueryParameters(Query q, Map params) {
			q.setProperties(params);
			return q;
		}
	}
	

	@Override
	public void save(E entity) {
		getSession().save(entity);
	}

	@Override
	public List<E> findAll() {
		return getSession().createCriteria(getEntityClass()).list();
	}

	@Override
	public E getById(PK id) {
		return (E) getSession().get(getEntityClass(), id);
	}

	@Override
	public void delete(Object entity) {
		getSession().delete(entity);
	}

	@Override
	public void delete(Serializable entity) {
		getSession().delete(entity);
	}

	@Override
	public void deleteById(PK id) {
		Object entity = getById(id);
		if (entity == null) {
			throw new ObjectRetrievalFailureException(getEntityClass(), id);
		}
		getSession().delete(entity);
	}

	@Override
	public void update(E entity) {
		getSession().update(entity);
	}

	@Override
	public void saveOrUpdate(E entity) {
		getSession().saveOrUpdate(entity);
	}

	@Override
	public void refresh(Object entity) {
		getSession().refresh(entity);
	}
	
	@Override
	public void deleteAll(Collection entities) {
		for (Object entity : entities) {
			this.getSession().delete(entity);
		}
	}
	
	
	
	public String generateQueryCondition(BaseQuery baseQuery,String extraCondition,String[] extraProperties, Object[] extraValues){
		BaseQuery clone = (BaseQuery) baseQuery.clone();
		StringBuilder sb = new StringBuilder();

		PropertyDescriptor[] pds;
		try {
			pds = Introspector.getBeanInfo(baseQuery.getClass()).getPropertyDescriptors();
			if (pds != null) {
				for (PropertyDescriptor pd : pds) {

					Method readMethod = pd.getReadMethod();
					if(readMethod == null){
						System.out.println("");
					}
					ColumnQuery c = readMethod.getAnnotation(ColumnQuery.class);

					if (c != null) {
						String column = c.name();
						if (column == null || column.equals("")) {
							column = pd.getName();
						}
						if ("like".equalsIgnoreCase(c.operator())) {
							try {
								pd.getWriteMethod().invoke(clone, "%" + pd.getValue(pd.getName()) + "%");
							} catch (Exception e) {
								e.printStackTrace();
							}
							sb.append("/~ and t." + column + " " + c.operator() + " '%[" + pd.getName() + "]%' ~/ ");

						}else if("in".equalsIgnoreCase(c.operator())) {
							sb.append("/~ and t." + column + " " + c.operator() + " {" + pd.getName() + "} ~/ ");
						}
						else {
							sb.append("/~ and t." + column + " " + c.operator() + " {" + pd.getName() + "} ~/ ");
						}
					}
				}
			}

			if(extraCondition != null && !extraCondition.trim().equals("")){
				sb.append(extraCondition);
				
			}
			sb.append("/~ order by [sortColumns] ~/");
			return sb.toString();
		}catch (IntrospectionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}
	
}
